/*+***********************************************************************************
 Filename: 20k_mcu02_mycore02\src\top.v
 Description: a simple MCU with rom, ram and peripheral. with interruption. with JTAG.

 Modification:
   2025.08.16 Creation   H.Zheng
              porting from previous design. async rom.
   2025.08.17 add pll, sync rom. (mcu01a_mycore01)
   2025.08.18 add jtag
   2025.10.25 porting from 9k_mcu02_mycore02, add more buttons.

Copyright (C) 2024-2025  Zheng Hui (hzheng@gzhu.edu.cn)

License: MulanPSL-2.0

***********************************************************************************-*/

module top (
  input wire [4:0] button,
  input wire sys_clk,
  output wire [5:0] led,
  input wire uart0_rxd,
  output wire uart0_txd,
  //
  input wire jtag_TCK,
  input wire jtag_TMS,
  input wire jtag_TDI,
  output wire jtag_TDO
);
  localparam ROM_SIZE_IN_KB = 16;
  localparam RAM_SIZE_IN_KB = 16;

  //reset signals
  wire reset_n = button[4];
  wire jtag_reset_n = button[3];

  //PLL to generate clk for ROM and RAM
  wire clk_192MHz, clk_32MHz;
  Gowin_rPLL_192M_32M mpll(
    .clkout(clk_192MHz), //output clkout
    .clkoutd(clk_32MHz), //output clkoutd
    .clkin(sys_clk) //input clkin
  );

  //jtag clk
  reg [7:0] jtagclk_counter;
  always @(posedge clk_32MHz) begin
    jtagclk_counter <= jtagclk_counter + 1'b1;
  end
  wire clk_1MHz = jtagclk_counter[4];

//  wire core_clk = clk_32MHz;
  wire core_clk = jtagclk_counter[0]; //16MHz

  //core
  wire [31:0] ibus_addr;
  wire [31:0] instruction;
  wire [31:0] monitor_port;
  wire [31:0] core_loadstore_addr;
  wire [31:0] core_loadstore_data_in;
  wire [31:0] core_loadstore_data_out;
  wire core_loadstore_ce;
  wire core_loadstore_we;
  wire [3:0] core_loadstore_wmask;
  wire [31:0] o_ext_csr0;

  wire jtag_halt_req_o;
  wire ext_int_req_i;


  zh_core_v02 m_core(
//    .clk(sys_clk),
    .clk(core_clk),
    .rst_n(reset_n),

    .ibus_addr(ibus_addr),
    .instruction_i(instruction),

    .core_loadstore_addr(core_loadstore_addr),
    .core_loadstore_data_in(core_loadstore_data_in),
    .core_loadstore_data_out(core_loadstore_data_out),
    .core_loadstore_ce(core_loadstore_ce),
    .core_loadstore_we(core_loadstore_we),
    .core_loadstore_wmask(core_loadstore_wmask),

    .halt_req_i(jtag_halt_req_o),
    .ext_int_req_i(ext_int_req_i),

    .monitor_port(monitor_port),
    .o_ext_csr0(o_ext_csr0)
 );


  //jtag

  wire jtag_mem_we_o;
  wire [31:0] jtag_mem_addr_o;    
  wire [31:0] jtag_mem_data_i;
  wire [31:0] jtag_mem_data_o;
  wire jtag_debug_req;

  jtag_top #(
    .DMI_ADDR_BITS(6),
    .DMI_DATA_BITS(32),
    .DMI_OP_BITS(2)
  ) u_jtag_top(
    .clk(clk_1MHz),
    .jtag_rst_n(jtag_reset_n),
    .jtag_pin_TCK(jtag_TCK),
    .jtag_pin_TMS(jtag_TMS),
    .jtag_pin_TDI(jtag_TDI),
    .jtag_pin_TDO(jtag_TDO),
    .reg_we_o(),
    .reg_addr_o(),
    .reg_wdata_o(),
    .reg_rdata_i(32'b0),
    .mem_we_o(jtag_mem_we_o),
    .mem_addr_o(jtag_mem_addr_o),
    .mem_wdata_o(jtag_mem_data_o),
    .mem_rdata_i(jtag_mem_data_i),
    .op_req_o(jtag_debug_req),
    .halt_req_o(jtag_halt_req_o),
    .reset_req_o()
  );

  //load/store bus multiplexer
  wire mux_d0_en = core_loadstore_ce & (core_loadstore_addr[31:28] == 4'b0000);
  wire mux_d1_en = core_loadstore_ce & (core_loadstore_addr[31:28] == 4'b0001);
  wire mux_d2_en = core_loadstore_ce & (core_loadstore_addr[31:28] == 4'b0010);

  wire[31:0] mux_d0_data_i, mux_d1_data_i, mux_d2_data_i;
  wire[31:0] mux_d0_addr_o, mux_d1_addr_o, mux_d2_addr_o;
  wire[31:0] mux_d1_data_o, mux_d2_data_o;

  assign core_loadstore_data_in = mux_d0_en ? mux_d0_data_i :
                     mux_d1_en ? mux_d1_data_i :
                     mux_d2_en ? mux_d2_data_i : 32'b0;
  assign mux_d0_addr_o = (mux_d0_en) ? core_loadstore_addr : 32'b0;
  assign mux_d1_addr_o = (mux_d1_en) ? core_loadstore_addr : 32'b0;
  assign mux_d2_addr_o = (mux_d2_en) ? core_loadstore_addr : 32'b0;
  assign mux_d1_data_o = (mux_d1_en) ? core_loadstore_data_out : 32'b0;
  assign mux_d2_data_o = (mux_d2_en) ? core_loadstore_data_out : 32'b0;

  wire jtag_d0_en = jtag_halt_req_o & (jtag_mem_addr_o[31:28] == 4'b0000);
  wire rom_en = (jtag_halt_req_o) ? jtag_d0_en : mux_d0_en;

  wire[31:0] rom_addr = (jtag_halt_req_o) ? jtag_mem_addr_o : mux_d0_addr_o;

  assign jtag_mem_data_i = mux_d0_data_i;

  wire rom_rclkb = (jtag_halt_req_o) ? core_clk : clk_192MHz;


  //rom

  zh_dprom_v02 #(.ROM_SIZE_IN_KB(ROM_SIZE_IN_KB)) I_ROM(
    .clka(clk_192MHz),
    .cea(1'b1),
    .addra(ibus_addr[clogb2(ROM_SIZE_IN_KB*256-1)+1:2]), 
    .douta(instruction),
    .clkb(rom_rclkb),
    .ceb(rom_en),
    .addrb(rom_addr[clogb2(ROM_SIZE_IN_KB*256-1)+1:2]), 
    .doutb(mux_d0_data_i),
    .wclkb(~clk_1MHz),
    .wenb(jtag_mem_we_o),
    .dinb(jtag_mem_data_o)
  );

  //sram
  zh_sram_v01 #(.RAM_SIZE_IN_KB(RAM_SIZE_IN_KB)) D_RAM(
    .clk(clk_192MHz),
    .ce(mux_d2_en),
    .addr(mux_d2_addr_o[clogb2(RAM_SIZE_IN_KB*256-1)+1:2]), 
    .dout(mux_d2_data_i),
    .wclk(core_clk),
    .wen(core_loadstore_we),
    .wmask(core_loadstore_wmask),
    .din(mux_d2_data_o)
  );

  //peripherals
  wire [5:0] peri_led;
  wire peri_txd;    

  zh_peripheral_v011 #(.CLK_FREQ_IN_MHz(16)) m_peripheral(
    .clk(core_clk),
    .reset_n(reset_n),
    .button(button[2:0]), 
    .led(peri_led),
    .rxd(uart0_rxd),
    .txd(uart0_txd),
    //load/store bus
    .ce(mux_d1_en),
    .wre(core_loadstore_we),
    .addr(mux_d1_addr_o[15:2]),
    .data_in(mux_d1_data_o),
    .data_out(mux_d1_data_i),
    .int_req_flag(ext_int_req_i)
  );

  //output
  assign led = peri_led;
//  assign led = ~monitor_port[29:24];
//  assign led = ~monitor_port[5:0];
//  assign led = ~o_ext_csr0[5:0];

  //
  function integer clogb2;
    input integer depth;
      for (clogb2=0; depth>0; clogb2=clogb2+1)
        depth = depth >> 1;
  endfunction

endmodule